//! Partial value construction for dynamic reflection
//!
//! This module provides APIs for incrementally building values through reflection,
//! particularly useful when deserializing data from external formats like JSON or YAML.
//!
//! # Overview
//!
//! The `Partial` type (formerly known as `Wip` - Work In Progress) allows you to:
//! - Allocate memory for a value based on its `Shape`
//! - Initialize fields incrementally in a type-safe manner
//! - Handle complex nested structures including structs, enums, collections, and smart pointers
//! - Build the final value once all required fields are initialized
//!
//! **Note**: This is the only API for partial value construction. The previous `TypedPartial`
//! wrapper has been removed in favor of using `Partial` directly.
//!
//! # Basic Usage
//!
//! ```no_run
//! # use facet_reflect::Partial;
//! # use facet_core::{Shape, Facet};
//! # fn example<T: Facet<'static>>() -> Result<(), Box<dyn std::error::Error>> {
//! // Allocate memory for a struct
//! let mut partial = Partial::alloc::<T>()?;
//!
//! // Set simple fields
//! partial = partial.set_field("name", "Alice")?;
//! partial = partial.set_field("age", 30u32)?;
//!
//! // Work with nested structures
//! partial = partial.begin_field("address")?;
//! partial = partial.set_field("street", "123 Main St")?;
//! partial = partial.set_field("city", "Springfield")?;
//! partial = partial.end()?;
//!
//! // Build the final value
//! let value = partial.build()?;
//! # Ok(())
//! # }
//! ```
//!
//! # Chaining Style
//!
//! The API supports method chaining for cleaner code:
//!
//! ```no_run
//! # use facet_reflect::Partial;
//! # use facet_core::{Shape, Facet};
//! # fn example<T: Facet<'static>>() -> Result<(), Box<dyn std::error::Error>> {
//! let value = Partial::alloc::<T>()?
//!     .set_field("name", "Bob")?
//!     .begin_field("scores")?
//!         .set(vec![95, 87, 92])?
//!     .end()?
//!     .build()?;
//! # Ok(())
//! # }
//! ```
//!
//! # Working with Collections
//!
//! ```no_run
//! # use facet_reflect::Partial;
//! # use facet_core::{Shape, Facet};
//! # fn example() -> Result<(), Box<dyn std::error::Error>> {
//! let mut partial = Partial::alloc::<Vec<String>>()?;
//!
//! // Add items to a list
//! partial = partial.begin_list_item()?;
//! partial = partial.set("first")?;
//! partial = partial.end()?;
//!
//! partial = partial.begin_list_item()?;
//! partial = partial.set("second")?;
//! partial = partial.end()?;
//!
//! let vec = partial.build()?;
//! # Ok(())
//! # }
//! ```
//!
//! # Working with Maps
//!
//! ```no_run
//! # use facet_reflect::Partial;
//! # use facet_core::{Shape, Facet};
//! # use std::collections::HashMap;
//! # fn example() -> Result<(), Box<dyn std::error::Error>> {
//! let mut partial = Partial::alloc::<HashMap<String, i32>>()?;
//!
//! // Insert key-value pairs
//! partial = partial.begin_key()?;
//! partial = partial.set("score")?;
//! partial = partial.end()?;
//! partial = partial.begin_value()?;
//! partial = partial.set(100i32)?;
//! partial = partial.end()?;
//!
//! let map = partial.build()?;
//! # Ok(())
//! # }
//! ```
//!
//! # Safety and Memory Management
//!
//! The `Partial` type ensures memory safety by:
//! - Tracking initialization state of all fields
//! - Preventing use-after-build through state tracking
//! - Properly handling drop semantics for partially initialized values
//! - Supporting both owned and borrowed values through lifetime parameters

use alloc::{collections::BTreeMap, vec::Vec};

mod iset;

mod partial_api;

use crate::{KeyPath, ReflectError, Resolution, TrackerKind, trace};

use core::{marker::PhantomData, ptr::NonNull};

mod heap_value;
pub use heap_value::*;

use facet_core::{
    Def, EnumType, Field, PtrMut, PtrUninit, Shape, SliceBuilderVTable, Type, UserType, Variant,
};
use iset::ISet;

/// State of a partial value
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum PartialState {
    /// Partial is active and can be modified
    Active,

    /// Partial has been successfully built and cannot be reused
    Built,
}

/// Mode of operation for frame management.
///
/// In `Strict` mode, frames must be fully initialized before being popped.
/// In `Deferred` mode, frames can be stored when popped and restored on re-entry,
/// with final validation happening in `finish_deferred()`.
enum FrameMode {
    /// Strict mode: frames must be fully initialized before popping.
    Strict {
        /// Stack of frames for nested initialization.
        stack: Vec<Frame>,
    },

    /// Deferred mode: frames are stored when popped, can be re-entered.
    Deferred {
        /// Stack of frames for nested initialization.
        stack: Vec<Frame>,

        /// The resolution from facet-solver describing the field structure.
        resolution: Resolution,

        /// The frame depth when deferred mode was started.
        /// Path calculations are relative to this depth.
        start_depth: usize,

        /// Current path as we navigate (e.g., ["inner", "x"]).
        // TODO: Intern key paths to avoid repeated allocations. The Resolution
        // already knows all possible paths, so we could use indices into that.
        current_path: KeyPath,

        /// Frames saved when popped, keyed by their path.
        /// When we re-enter a path, we restore the stored frame.
        // TODO: Consider using path indices instead of cloned KeyPaths as keys.
        stored_frames: BTreeMap<KeyPath, Frame>,
    },
}

impl FrameMode {
    /// Get a reference to the frame stack.
    fn stack(&self) -> &Vec<Frame> {
        match self {
            FrameMode::Strict { stack } | FrameMode::Deferred { stack, .. } => stack,
        }
    }

    /// Get a mutable reference to the frame stack.
    fn stack_mut(&mut self) -> &mut Vec<Frame> {
        match self {
            FrameMode::Strict { stack } | FrameMode::Deferred { stack, .. } => stack,
        }
    }

    /// Check if we're in deferred mode.
    fn is_deferred(&self) -> bool {
        matches!(self, FrameMode::Deferred { .. })
    }

    /// Get the start depth if in deferred mode.
    fn start_depth(&self) -> Option<usize> {
        match self {
            FrameMode::Deferred { start_depth, .. } => Some(*start_depth),
            FrameMode::Strict { .. } => None,
        }
    }

    /// Get the current path if in deferred mode.
    fn current_path(&self) -> Option<&KeyPath> {
        match self {
            FrameMode::Deferred { current_path, .. } => Some(current_path),
            FrameMode::Strict { .. } => None,
        }
    }

    /// Get the resolution if in deferred mode.
    fn resolution(&self) -> Option<&Resolution> {
        match self {
            FrameMode::Deferred { resolution, .. } => Some(resolution),
            FrameMode::Strict { .. } => None,
        }
    }
}

/// A type-erased, heap-allocated, partially-initialized value.
///
/// [Partial] keeps track of the state of initialiation of the underlying
/// value: if we're building `struct S { a: u32, b: String }`, we may
/// have initialized `a`, or `b`, or both, or neither.
///
/// [Partial] allows navigating down nested structs and initializing them
/// progressively: [Partial::begin_field] pushes a frame onto the stack,
/// which then has to be initialized, and popped off with [Partial::end].
///
/// If [Partial::end] is called but the current frame isn't fully initialized,
/// an error is returned: in other words, if you navigate down to a field,
/// you have to fully initialize it one go. You can't go back up and back down
/// to it again.
pub struct Partial<'facet, const BORROW: bool = true> {
    /// Frame management mode (strict or deferred) and associated state.
    mode: FrameMode,

    /// current state of the Partial
    state: PartialState,

    invariant: PhantomData<fn(&'facet ()) -> &'facet ()>,
}

#[derive(Clone, Copy, Debug)]
pub(crate) enum MapInsertState {
    /// Not currently inserting
    Idle,

    /// Pushing key - memory allocated, waiting for initialization
    PushingKey {
        /// Temporary storage for the key being built
        key_ptr: PtrUninit<'static>,
        /// Whether the key has been fully initialized
        key_initialized: bool,
    },

    /// Pushing value after key is done
    PushingValue {
        /// Temporary storage for the key that was built (always initialized)
        key_ptr: PtrUninit<'static>,
        /// Temporary storage for the value being built
        value_ptr: Option<PtrUninit<'static>>,
        /// Whether the value has been fully initialized
        value_initialized: bool,
    },
}

#[derive(Debug, Clone, Copy)]
pub(crate) enum FrameOwnership {
    /// This frame owns the allocation and should deallocate it on drop
    Owned,

    /// This frame points to a field/element within a parent's allocation.
    /// The parent's `iset\[field_idx\]` was CLEARED when this frame was created.
    /// On drop: deinit if initialized, but do NOT deallocate.
    /// On successful end(): parent's `iset\[field_idx\]` will be SET.
    Field { field_idx: usize },

    /// This frame's allocation is managed elsewhere (e.g., in MapInsertState)
    ManagedElsewhere,
}

/// Points somewhere in a partially-initialized value. If we're initializing
/// `a.b.c`, then the first frame would point to the beginning of `a`, the
/// second to the beginning of the `b` field of `a`, etc.
///
/// A frame can point to a complex data structure, like a struct or an enum:
/// it keeps track of whether a variant was selected, which fields are initialized,
/// etc. and is able to drop & deinitialize
#[must_use]
pub(crate) struct Frame {
    /// Address of the value being initialized
    pub(crate) data: PtrUninit<'static>,

    /// Shape of the value being initialized
    pub(crate) shape: &'static Shape,

    /// Whether this frame's data is fully initialized
    pub(crate) is_init: bool,

    /// Tracks building mode and partial initialization state
    pub(crate) tracker: Tracker,

    /// Whether this frame owns the allocation or is just a field pointer
    pub(crate) ownership: FrameOwnership,

    /// Whether this frame is for a custom deserialization pipeline
    pub(crate) using_custom_deserialization: bool,

    /// If this frame was created for shape-level proxy deserialization,
    /// this stores the ProxyDef for the conversion in end().
    #[cfg(feature = "alloc")]
    pub(crate) shape_level_proxy: Option<&'static facet_core::ProxyDef>,
}

#[derive(Debug)]
pub(crate) enum Tracker {
    /// Simple scalar value - no partial initialization tracking needed.
    /// Whether it's initialized is tracked by `Frame::is_init`.
    Scalar,

    /// Partially initialized array
    Array {
        /// Track which array elements are initialized (up to 63 elements)
        iset: ISet,
        /// If we're pushing another frame, this is set to the array index
        current_child: Option<usize>,
    },

    /// Partially initialized struct/tuple-struct etc.
    Struct {
        /// fields need to be individually tracked — we only
        /// support up to 63 fields.
        iset: ISet,
        /// if we're pushing another frame, this is set to the index of the struct field
        current_child: Option<usize>,
    },

    /// Smart pointer being initialized.
    /// Whether it's initialized is tracked by `Frame::is_init`.
    SmartPointer,

    /// We're initializing an `Arc<[T]>`, `Box<[T]>`, `Rc<[T]>`, etc.
    ///
    /// We're using the slice builder API to construct the slice
    SmartPointerSlice {
        /// The slice builder vtable
        vtable: &'static SliceBuilderVTable,

        /// Whether we're currently building an item to push
        building_item: bool,
    },

    /// Partially initialized enum (but we picked a variant,
    /// so it's not Uninit)
    Enum {
        /// Variant chosen for the enum
        variant: &'static Variant,
        /// tracks enum fields (for the given variant)
        data: ISet,
        /// If we're pushing another frame, this is set to the field index
        current_child: Option<usize>,
    },

    /// Partially initialized list (Vec, etc.)
    /// Whether it's initialized is tracked by `Frame::is_init`.
    List {
        /// If we're pushing another frame for an element
        current_child: bool,
    },

    /// Partially initialized map (HashMap, BTreeMap, etc.)
    /// Whether it's initialized is tracked by `Frame::is_init`.
    Map {
        /// State of the current insertion operation
        insert_state: MapInsertState,
    },

    /// Partially initialized set (HashSet, BTreeSet, etc.)
    /// Whether it's initialized is tracked by `Frame::is_init`.
    Set {
        /// If we're pushing another frame for an element
        current_child: bool,
    },

    /// Option being initialized with Some(inner_value)
    Option {
        /// Whether we're currently building the inner value
        building_inner: bool,
    },

    /// Result being initialized with Ok or Err
    Result {
        /// Whether we're building Ok (true) or Err (false)
        is_ok: bool,
        /// Whether we're currently building the inner value
        building_inner: bool,
    },

    /// Dynamic value (e.g., facet_value::Value) being initialized
    DynamicValue {
        /// What kind of dynamic value we're building
        state: DynamicValueState,
    },
}

/// State for building a dynamic value
#[derive(Debug)]
#[allow(dead_code)] // Some variants are for future use (object support)
pub(crate) enum DynamicValueState {
    /// Not yet initialized - will be set to scalar, array, or object
    Uninit,
    /// Initialized as a scalar (null, bool, number, string, bytes)
    Scalar,
    /// Initialized as an array, currently building an element
    Array { building_element: bool },
    /// Initialized as an object
    Object {
        insert_state: DynamicObjectInsertState,
    },
}

/// State for inserting into a dynamic object
#[derive(Debug)]
#[allow(dead_code)] // For future use (object support)
pub(crate) enum DynamicObjectInsertState {
    /// Idle - ready for a new key-value pair
    Idle,
    /// Currently building the value for a key
    BuildingValue {
        /// The key for the current entry
        key: alloc::string::String,
    },
}

impl Tracker {
    fn kind(&self) -> TrackerKind {
        match self {
            Tracker::Scalar => TrackerKind::Scalar,
            Tracker::Array { .. } => TrackerKind::Array,
            Tracker::Struct { .. } => TrackerKind::Struct,
            Tracker::SmartPointer => TrackerKind::SmartPointer,
            Tracker::SmartPointerSlice { .. } => TrackerKind::SmartPointerSlice,
            Tracker::Enum { .. } => TrackerKind::Enum,
            Tracker::List { .. } => TrackerKind::List,
            Tracker::Map { .. } => TrackerKind::Map,
            Tracker::Set { .. } => TrackerKind::Set,
            Tracker::Option { .. } => TrackerKind::Option,
            Tracker::Result { .. } => TrackerKind::Result,
            Tracker::DynamicValue { .. } => TrackerKind::DynamicValue,
        }
    }

    /// Set the current_child index for trackers that support it
    fn set_current_child(&mut self, idx: usize) {
        match self {
            Tracker::Struct { current_child, .. }
            | Tracker::Enum { current_child, .. }
            | Tracker::Array { current_child, .. } => {
                *current_child = Some(idx);
            }
            _ => {}
        }
    }

    /// Clear the current_child index for trackers that support it
    fn clear_current_child(&mut self) {
        match self {
            Tracker::Struct { current_child, .. }
            | Tracker::Enum { current_child, .. }
            | Tracker::Array { current_child, .. } => {
                *current_child = None;
            }
            _ => {}
        }
    }
}

impl Frame {
    fn new(data: PtrUninit<'static>, shape: &'static Shape, ownership: FrameOwnership) -> Self {
        // For empty structs (structs with 0 fields), start as initialized since there's nothing to initialize
        // This includes empty tuples () which are zero-sized types with no fields to initialize
        let is_init = matches!(
            shape.ty,
            Type::User(UserType::Struct(struct_type)) if struct_type.fields.is_empty()
        );

        Self {
            data,
            shape,
            is_init,
            tracker: Tracker::Scalar,
            ownership,
            using_custom_deserialization: false,
            #[cfg(feature = "alloc")]
            shape_level_proxy: None,
        }
    }

    /// Deinitialize any initialized field: calls `drop_in_place` but does not free any
    /// memory even if the frame owns that memory.
    ///
    /// After this call, `is_init` will be false and `tracker` will be [Tracker::Scalar].
    fn deinit(&mut self) {
        // For ManagedElsewhere frames, the parent owns the value and is responsible
        // for dropping it. We should not drop it here to avoid double-free.
        if matches!(self.ownership, FrameOwnership::ManagedElsewhere) {
            self.is_init = false;
            self.tracker = Tracker::Scalar;
            return;
        }

        match &self.tracker {
            Tracker::Scalar => {
                // Simple scalar - drop if initialized
                if self.is_init {
                    if let Some(drop_fn) = self.shape.vtable.drop_in_place {
                        unsafe { drop_fn(self.data.assume_init()) };
                    }
                }
            }
            Tracker::Array { iset, .. } => {
                // Drop initialized array elements
                if let Type::Sequence(facet_core::SequenceType::Array(array_def)) = self.shape.ty {
                    let element_layout = array_def.t.layout.sized_layout().ok();
                    if let Some(layout) = element_layout {
                        for idx in 0..array_def.n {
                            if iset.get(idx) {
                                let offset = layout.size() * idx;
                                let element_ptr = unsafe { self.data.field_init_at(offset) };
                                if let Some(drop_fn) = array_def.t.vtable.drop_in_place {
                                    unsafe { drop_fn(element_ptr) };
                                }
                            }
                        }
                    }
                }
            }
            Tracker::Struct { iset, .. } => {
                // Drop initialized struct fields
                if let Type::User(UserType::Struct(struct_type)) = self.shape.ty {
                    if iset.all_set() && self.shape.vtable.drop_in_place.is_some() {
                        unsafe {
                            (self.shape.vtable.drop_in_place.unwrap())(self.data.assume_init())
                        };
                    } else {
                        for (idx, field) in struct_type.fields.iter().enumerate() {
                            if iset.get(idx) {
                                // This field was initialized, drop it
                                let field_ptr = unsafe { self.data.field_init_at(field.offset) };
                                if let Some(drop_fn) = field.shape().vtable.drop_in_place {
                                    unsafe { drop_fn(field_ptr) };
                                }
                            }
                        }
                    }
                }
            }
            Tracker::Enum { variant, data, .. } => {
                // Drop initialized enum variant fields
                for (idx, field) in variant.data.fields.iter().enumerate() {
                    if data.get(idx) {
                        // This field was initialized, drop it
                        let field_ptr = unsafe { self.data.as_mut_byte_ptr().add(field.offset) };
                        if let Some(drop_fn) = field.shape().vtable.drop_in_place {
                            unsafe { drop_fn(PtrMut::new(NonNull::new_unchecked(field_ptr))) };
                        }
                    }
                }
            }
            Tracker::SmartPointer => {
                // Drop the initialized Box
                if self.is_init {
                    if let Some(drop_fn) = self.shape.vtable.drop_in_place {
                        unsafe { drop_fn(self.data.assume_init()) };
                    }
                }
                // Note: we don't deallocate the inner value here because
                // the Box's drop will handle that
            }
            Tracker::SmartPointerSlice { vtable, .. } => {
                // Free the slice builder
                let builder_ptr = unsafe { self.data.assume_init() };
                unsafe {
                    (vtable.free_fn)(builder_ptr);
                }
            }
            Tracker::List { .. } => {
                // Drop the initialized List
                if self.is_init {
                    if let Some(drop_fn) = self.shape.vtable.drop_in_place {
                        unsafe { drop_fn(self.data.assume_init()) };
                    }
                }
            }
            Tracker::Map { insert_state } => {
                // Drop the initialized Map
                if self.is_init {
                    if let Some(drop_fn) = self.shape.vtable.drop_in_place {
                        unsafe { drop_fn(self.data.assume_init()) };
                    }
                }

                // Clean up any in-progress insertion state
                match insert_state {
                    MapInsertState::PushingKey {
                        key_ptr,
                        key_initialized,
                    } => {
                        if let Def::Map(map_def) = self.shape.def {
                            // Drop the key if it was initialized
                            if *key_initialized {
                                if let Some(drop_fn) = map_def.k().vtable.drop_in_place {
                                    unsafe { drop_fn(key_ptr.assume_init()) };
                                }
                            }
                            // Deallocate the key buffer
                            if let Ok(key_shape) = map_def.k().layout.sized_layout() {
                                if key_shape.size() > 0 {
                                    unsafe {
                                        alloc::alloc::dealloc(key_ptr.as_mut_byte_ptr(), key_shape)
                                    };
                                }
                            }
                        }
                    }
                    MapInsertState::PushingValue {
                        key_ptr,
                        value_ptr,
                        value_initialized,
                    } => {
                        // Drop and deallocate both key and value buffers
                        if let Def::Map(map_def) = self.shape.def {
                            // Drop and deallocate the key (always initialized in PushingValue state)
                            if let Some(drop_fn) = map_def.k().vtable.drop_in_place {
                                unsafe { drop_fn(key_ptr.assume_init()) };
                            }
                            if let Ok(key_shape) = map_def.k().layout.sized_layout() {
                                if key_shape.size() > 0 {
                                    unsafe {
                                        alloc::alloc::dealloc(key_ptr.as_mut_byte_ptr(), key_shape)
                                    };
                                }
                            }

                            // Handle the value if it exists
                            if let Some(value_ptr) = value_ptr {
                                // Drop the value if it was initialized
                                if *value_initialized {
                                    if let Some(drop_fn) = map_def.v().vtable.drop_in_place {
                                        unsafe { drop_fn(value_ptr.assume_init()) };
                                    }
                                }
                                // Deallocate the value buffer
                                if let Ok(value_shape) = map_def.v().layout.sized_layout() {
                                    if value_shape.size() > 0 {
                                        unsafe {
                                            alloc::alloc::dealloc(
                                                value_ptr.as_mut_byte_ptr(),
                                                value_shape,
                                            )
                                        };
                                    }
                                }
                            }
                        }
                    }
                    MapInsertState::Idle => {}
                }
            }
            Tracker::Set { .. } => {
                // Drop the initialized Set
                if self.is_init {
                    if let Some(drop_fn) = self.shape.vtable.drop_in_place {
                        unsafe { drop_fn(self.data.assume_init()) };
                    }
                }
            }
            Tracker::Option { building_inner } => {
                // If we're building the inner value, it will be handled by the Option vtable
                // No special cleanup needed here as the Option will either be properly
                // initialized or remain uninitialized
                if !building_inner {
                    // Option is fully initialized, drop it normally
                    if let Some(drop_fn) = self.shape.vtable.drop_in_place {
                        unsafe { drop_fn(self.data.assume_init()) };
                    }
                }
            }
            Tracker::Result { building_inner, .. } => {
                // If we're building the inner value, it will be handled by the Result vtable
                // No special cleanup needed here as the Result will either be properly
                // initialized or remain uninitialized
                if !building_inner {
                    // Result is fully initialized, drop it normally
                    if let Some(drop_fn) = self.shape.vtable.drop_in_place {
                        unsafe { drop_fn(self.data.assume_init()) };
                    }
                }
            }
            Tracker::DynamicValue { .. } => {
                // Drop if initialized
                if self.is_init {
                    if let Some(drop_fn) = self.shape.vtable.drop_in_place {
                        unsafe { drop_fn(self.data.assume_init()) };
                    }
                }
            }
        }

        self.is_init = false;
        self.tracker = Tracker::Scalar;
    }

    /// This must be called after (fully) initializing a value.
    ///
    /// This sets `is_init` to `true` to indicate the value is initialized.
    /// Composite types (structs, enums, etc.) might be handled differently.
    ///
    /// # Safety
    ///
    /// This should only be called when `self.data` has been actually initialized.
    unsafe fn mark_as_init(&mut self) {
        self.is_init = true;
    }

    /// Deallocate the memory associated with this frame, if it owns it.
    ///
    /// The memory has to be deinitialized first, see [Frame::deinit]
    fn dealloc(self) {
        if self.is_init {
            unreachable!("a frame has to be deinitialized before being deallocated")
        }

        // Now, deallocate temporary String allocation if necessary
        if let FrameOwnership::Owned = self.ownership {
            if let Ok(layout) = self.shape.layout.sized_layout() {
                if layout.size() > 0 {
                    unsafe { alloc::alloc::dealloc(self.data.as_mut_byte_ptr(), layout) };
                }
            }
            // no need to update `self.ownership` since `self` drops at the end of this
        }
    }

    /// Fill in defaults for any unset fields that have default values.
    ///
    /// This handles:
    /// - Container-level defaults (when no fields set and struct has Default impl)
    /// - Fields with `#[facet(default = ...)]` - uses the explicit default function
    /// - Fields with `#[facet(default)]` - uses the type's Default impl
    /// - `Option<T>` fields - default to None
    ///
    /// Returns Ok(()) if successful, or an error if a field has `#[facet(default)]`
    /// but no default implementation is available.
    fn fill_defaults(&mut self) -> Result<(), ReflectError> {
        // First, check if we need to upgrade from Scalar to Struct tracker
        // This happens when no fields were visited at all in deferred mode
        if !self.is_init && matches!(self.tracker, Tracker::Scalar) {
            if let Type::User(UserType::Struct(struct_type)) = self.shape.ty {
                // If no fields were visited and the container has a default, use it
                if let Some(default_fn) = self.shape.vtable.default_in_place {
                    unsafe { default_fn(self.data) };
                    self.is_init = true;
                    return Ok(());
                }
                // Otherwise initialize the struct tracker with empty iset
                self.tracker = Tracker::Struct {
                    iset: ISet::new(struct_type.fields.len()),
                    current_child: None,
                };
            }
        }

        match &mut self.tracker {
            Tracker::Struct { iset, .. } => {
                if let Type::User(UserType::Struct(struct_type)) = self.shape.ty {
                    // Check if NO fields have been set and the container has a default
                    let no_fields_set = (0..struct_type.fields.len()).all(|i| !iset.get(i));
                    if no_fields_set {
                        if let Some(default_fn) = self.shape.vtable.default_in_place {
                            unsafe { default_fn(self.data) };
                            self.tracker = Tracker::Scalar;
                            self.is_init = true;
                            return Ok(());
                        }
                    }

                    // Fill defaults for individual fields
                    for (idx, field) in struct_type.fields.iter().enumerate() {
                        // Skip already-initialized fields
                        if iset.get(idx) {
                            continue;
                        }

                        // Check if field has a default we can use
                        if let Some(default_fn) = Self::get_field_default_fn(field) {
                            // Calculate field pointer
                            let field_ptr = unsafe { self.data.field_uninit_at(field.offset) };

                            // Call the default function to initialize the field
                            unsafe { default_fn(field_ptr) };

                            // Mark field as initialized
                            iset.set(idx);
                        } else if field.has_default() {
                            // Field has #[facet(default)] but we couldn't find a default function.
                            // This happens with opaque types that don't have default_in_place.
                            return Err(ReflectError::DefaultAttrButNoDefaultImpl {
                                shape: field.shape(),
                            });
                        }
                    }
                }
            }
            Tracker::Enum { variant, data, .. } => {
                // Handle enum variant fields
                for (idx, field) in variant.data.fields.iter().enumerate() {
                    // Skip already-initialized fields
                    if data.get(idx) {
                        continue;
                    }

                    // Check if field has a default we can use
                    if let Some(default_fn) = Self::get_field_default_fn(field) {
                        // Calculate field pointer within the variant data
                        let field_ptr = unsafe { self.data.field_uninit_at(field.offset) };

                        // Call the default function to initialize the field
                        unsafe { default_fn(field_ptr) };

                        // Mark field as initialized
                        data.set(idx);
                    } else if field.has_default() {
                        // Field has #[facet(default)] but we couldn't find a default function.
                        return Err(ReflectError::DefaultAttrButNoDefaultImpl {
                            shape: field.shape(),
                        });
                    }
                }
            }
            // Other tracker types don't have fields with defaults
            _ => {}
        }
        Ok(())
    }

    /// Get the default function for a field, if one is available.
    ///
    /// Priority:
    /// 1. Explicit field-level default_fn (from `#[facet(default = ...)]`)
    /// 2. Type-level default_in_place (from Default impl, including `Option<T>`)
    ///    but only if the field has the DEFAULT flag
    /// 3. Special cases: `Option<T>` (defaults to None), () (unit type)
    fn get_field_default_fn(field: &Field) -> Option<facet_core::DefaultInPlaceFn> {
        // First check for explicit field-level default
        if let Some(default_fn) = field.default_fn() {
            return Some(default_fn);
        }

        // Check if field has default attribute and type has default_in_place
        if field.has_default() {
            if let Some(default_fn) = field.shape().vtable.default_in_place {
                return Some(default_fn);
            }
        }

        // Special case: Option<T> always defaults to None, even without explicit #[facet(default)]
        // This is because Option is fundamentally "optional" - if not set, it should be None
        if matches!(field.shape().def, Def::Option(_)) {
            if let Some(default_fn) = field.shape().vtable.default_in_place {
                return Some(default_fn);
            }
        }

        // Special case: () unit type always defaults to ()
        if field.shape().is_type::<()>() {
            if let Some(default_fn) = field.shape().vtable.default_in_place {
                return Some(default_fn);
            }
        }

        None
    }

    /// Returns an error if the value is not fully initialized
    fn require_full_initialization(&self) -> Result<(), ReflectError> {
        match self.tracker {
            Tracker::Scalar => {
                if self.is_init {
                    Ok(())
                } else {
                    Err(ReflectError::UninitializedValue { shape: self.shape })
                }
            }
            Tracker::Array { iset, .. } => {
                match self.shape.ty {
                    Type::Sequence(facet_core::SequenceType::Array(array_def)) => {
                        // Check if all array elements are initialized
                        if (0..array_def.n).all(|idx| iset.get(idx)) {
                            Ok(())
                        } else {
                            Err(ReflectError::UninitializedValue { shape: self.shape })
                        }
                    }
                    _ => Err(ReflectError::UninitializedValue { shape: self.shape }),
                }
            }
            Tracker::Struct { iset, .. } => {
                if iset.all_set() {
                    Ok(())
                } else {
                    // Attempt to find the first uninitialized field, if possible
                    match self.shape.ty {
                        Type::User(UserType::Struct(struct_type)) => {
                            // Find index of the first bit not set
                            let first_missing_idx =
                                (0..struct_type.fields.len()).find(|&idx| !iset.get(idx));
                            if let Some(missing_idx) = first_missing_idx {
                                let field_name = struct_type.fields[missing_idx].name;
                                Err(ReflectError::UninitializedField {
                                    shape: self.shape,
                                    field_name,
                                })
                            } else {
                                // fallback, something went wrong
                                Err(ReflectError::UninitializedValue { shape: self.shape })
                            }
                        }
                        _ => Err(ReflectError::UninitializedValue { shape: self.shape }),
                    }
                }
            }
            Tracker::Enum { variant, data, .. } => {
                // Check if all fields of the variant are initialized
                let num_fields = variant.data.fields.len();
                if num_fields == 0 {
                    // Unit variant, always initialized
                    Ok(())
                } else if (0..num_fields).all(|idx| data.get(idx)) {
                    Ok(())
                } else {
                    // Find the first uninitialized field
                    let first_missing_idx = (0..num_fields).find(|&idx| !data.get(idx));
                    if let Some(missing_idx) = first_missing_idx {
                        let field_name = variant.data.fields[missing_idx].name;
                        Err(ReflectError::UninitializedEnumField {
                            shape: self.shape,
                            field_name,
                            variant_name: variant.name,
                        })
                    } else {
                        Err(ReflectError::UninitializedValue { shape: self.shape })
                    }
                }
            }
            Tracker::SmartPointer => {
                if self.is_init {
                    Ok(())
                } else {
                    Err(ReflectError::UninitializedValue { shape: self.shape })
                }
            }
            Tracker::SmartPointerSlice { building_item, .. } => {
                if building_item {
                    Err(ReflectError::UninitializedValue { shape: self.shape })
                } else {
                    Ok(())
                }
            }
            Tracker::List { current_child } => {
                if self.is_init && !current_child {
                    Ok(())
                } else {
                    Err(ReflectError::UninitializedValue { shape: self.shape })
                }
            }
            Tracker::Map { insert_state } => {
                if self.is_init && matches!(insert_state, MapInsertState::Idle) {
                    Ok(())
                } else {
                    Err(ReflectError::UninitializedValue { shape: self.shape })
                }
            }
            Tracker::Set { current_child } => {
                if self.is_init && !current_child {
                    Ok(())
                } else {
                    Err(ReflectError::UninitializedValue { shape: self.shape })
                }
            }
            Tracker::Option { building_inner } => {
                if building_inner {
                    Err(ReflectError::UninitializedValue { shape: self.shape })
                } else {
                    Ok(())
                }
            }
            Tracker::Result { building_inner, .. } => {
                if building_inner {
                    Err(ReflectError::UninitializedValue { shape: self.shape })
                } else {
                    Ok(())
                }
            }
            Tracker::DynamicValue { ref state } => {
                if matches!(state, DynamicValueState::Uninit) {
                    Err(ReflectError::UninitializedValue { shape: self.shape })
                } else {
                    Ok(())
                }
            }
        }
    }

    /// Get the [EnumType] of the frame's shape, if it is an enum type
    pub(crate) fn get_enum_type(&self) -> Result<EnumType, ReflectError> {
        match self.shape.ty {
            Type::User(UserType::Enum(e)) => Ok(e),
            _ => Err(ReflectError::WasNotA {
                expected: "enum",
                actual: self.shape,
            }),
        }
    }

    pub(crate) fn get_field(&self) -> Option<&Field> {
        match self.shape.ty {
            Type::User(user_type) => match user_type {
                UserType::Struct(struct_type) => {
                    // Try to get currently active field index
                    if let Tracker::Struct {
                        current_child: Some(idx),
                        ..
                    } = &self.tracker
                    {
                        struct_type.fields.get(*idx)
                    } else {
                        None
                    }
                }
                UserType::Enum(_enum_type) => {
                    if let Tracker::Enum {
                        variant,
                        current_child: Some(idx),
                        ..
                    } = &self.tracker
                    {
                        variant.data.fields.get(*idx)
                    } else {
                        None
                    }
                }
                _ => None,
            },
            _ => None,
        }
    }
}

// Convenience methods on Partial for accessing FrameMode internals.
// These help minimize changes to the rest of the codebase during the refactor.
impl<'facet, const BORROW: bool> Partial<'facet, BORROW> {
    /// Get a reference to the frame stack.
    #[inline]
    pub(crate) fn frames(&self) -> &Vec<Frame> {
        self.mode.stack()
    }

    /// Get a mutable reference to the frame stack.
    #[inline]
    pub(crate) fn frames_mut(&mut self) -> &mut Vec<Frame> {
        self.mode.stack_mut()
    }

    /// Check if we're in deferred mode.
    #[inline]
    pub fn is_deferred(&self) -> bool {
        self.mode.is_deferred()
    }

    /// Get the start depth if in deferred mode.
    #[inline]
    pub(crate) fn start_depth(&self) -> Option<usize> {
        self.mode.start_depth()
    }

    /// Get the current path if in deferred mode.
    #[inline]
    pub(crate) fn current_path(&self) -> Option<&KeyPath> {
        self.mode.current_path()
    }

    /// Get the resolution if in deferred mode.
    #[inline]
    pub(crate) fn resolution(&self) -> Option<&Resolution> {
        self.mode.resolution()
    }
}

impl<'facet, const BORROW: bool> Drop for Partial<'facet, BORROW> {
    fn drop(&mut self) {
        trace!("🧹 Partial is being dropped");

        // With the ownership transfer model:
        // - When we enter a field, parent's iset[idx] is cleared
        // - Parent won't try to drop fields with iset[idx] = false
        // - No double-free possible by construction

        // 1. Clean up stored frames from deferred state
        if let FrameMode::Deferred { stored_frames, .. } = &mut self.mode {
            // Stored frames have ownership of their data (parent's iset was cleared).
            for (_, mut frame) in core::mem::take(stored_frames) {
                // Always call deinit - it internally handles each tracker type:
                // - Scalar: checks is_init
                // - Struct/Array/Enum: uses iset to drop individual fields/elements
                frame.deinit();
                // Don't deallocate - Field ownership means parent owns the memory
            }
        }

        // 2. Pop and deinit stack frames
        while let Some(mut frame) = self.mode.stack_mut().pop() {
            // Always call deinit - it internally handles each tracker type correctly.
            // Parent's iset was cleared when we entered this field,
            // so parent won't try to drop it.
            frame.deinit();

            // Only deallocate if this frame owns the allocation
            if let FrameOwnership::Owned = frame.ownership {
                frame.dealloc();
            }
        }
    }
}
